#include <iostream>
#include <cmath>
#include <cstring>
#include <fstream>
#include <ctime>

namespace Matrix_NAMESPACE
{
    typedef long long ll;
    class Matrix
    {
    public:
        ll** mat;

    private:
        ll row, column;

    public:
        Matrix(/* args */);
        Matrix(ll _row, ll _column);
        Matrix(const Matrix& _a);
        ~Matrix();

        void setRC(ll _row, ll _column);
        void zero();
        friend Matrix operator*(const Matrix& a, const Matrix& b);
        Matrix& operator=(const Matrix& _a);
        void display();

    private:
        void destroyMat();
        void newMat();
        bool overflow(ll _row, ll _column);
        void copyFrom(const Matrix& _a);
    };

    Matrix::Matrix(/* args */) : row(0), column(0), mat(nullptr)
    {
    }

    Matrix::Matrix(ll _row, ll _column) : row(_row), column(_column)
    {
        this->newMat();
    }

    Matrix::Matrix(const Matrix& _a)
    {
        this->setRC(_a.row, _a.column);
        this->copyFrom(_a);
    }

    void Matrix::newMat()
    {
        mat = new ll * [row];
        for (ll i = 0; i < row; i++)
        {
            mat[i] = new ll[column];
        }
    }

    Matrix::~Matrix()
    {
        this->destroyMat();
    }

    void Matrix::destroyMat()
    {
        if (mat == nullptr)
            return;
        for (ll i = 0; i < row; i++)
        {
            delete[] mat[i];
        }
        delete[] mat;
        mat = nullptr;
        row = 0;
        column = 0;
    }

    void Matrix::setRC(ll _row, ll _column)
    {
        this->row = _row;
        this->column = _column;
        this->destroyMat();
        this->newMat();
    }

    bool Matrix::overflow(ll _row, ll _column)
    {
        if (_row >= this->row || _column >= this->column)
        {
            perror("Matrix overflow error.");
            return true;
        }
        else
            return false;
    }

    void Matrix::zero()
    {
        for (ll i = 0; i < this->row; i++)
        {
            memset(this->mat[i], 0, sizeof(ll) * this->column);
        }
    }

    Matrix operator*(const Matrix& a, const Matrix& b)
    {
        if (a.column != b.row || a.row <= 0 || a.column <= 0 || b.row <= 0 || b.column <= 0 || a.mat == nullptr || b.mat == nullptr)
        {
            perror("Can't multiply!");
            return Matrix();
        }
        Matrix c(a.row, b.column);
        c.zero();
        for (int i = 0; i < a.row; i++)
        {
            for (int j = 0; j < b.column; j++)
            {
                ll tmp = 0;
                for (int k = 0; k < a.column; k++)
                {
                    tmp += a.mat[i][k] * b.mat[k][j];
                }
                c.mat[i][j] = tmp;
            }
        }
        return c;
    }

    Matrix& Matrix::operator=(const Matrix& _a)
    {
        this->setRC(_a.row, _a.column);
        this->copyFrom(_a);
        return *this;
    }

    void Matrix::copyFrom(const Matrix& _a)
    {
        for (ll i = 0; i < _a.row; i++)
        {
            for (ll j = 0; j < _a.column; j++)
            {
                this->mat[i][j] = _a.mat[i][j];
            }
        }
    }

    void Matrix::display()
    {
        for (ll i = 0; i < this->row; i++)
        {
            for (ll j = 0; j < this->column; j++)
            {
                printf("%lld ", this->mat[i][j]);
            }
            printf("\n");
        }
    }

} // namespace Matrix_NAMESPACE

using namespace std;
using namespace Matrix_NAMESPACE;

void readMatrix(Matrix& a);

int main(int argc, char const* argv[])
{
    Matrix a;
    readMatrix(a);
    Matrix b = a;
    Matrix c = a * b;
    return 0;
}

void readMatrix(Matrix& a)
{
    int n_chars_of_line = 0;
    ifstream infile("E:\\aboutme\\github_code\\C - Programming - Learn\\RandomMatrix.txt");
    infile >> n_chars_of_line;
    a.setRC(n_chars_of_line, n_chars_of_line);
    for (size_t i = 0; i < n_chars_of_line; i++)
    {
        for (size_t j = 0; j < n_chars_of_line; j++)
        {
            infile >> a.mat[i][j];
        }
    }
}